library(tidyverse)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.2.1     ✓ purrr   0.3.3
✓ tibble  2.1.3     ✓ dplyr   0.8.4
✓ tidyr   1.0.2     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.5.0
── Conflicts ────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Purrrrrrrrrrrrrrrrrrrrrrr

map() is the main one.

Iterates over a data structure and runs a function that you provide on each “element” of the data structure. replaces ‘for’ loops.

map_() is data type they return e.g. map_dbl() return a vector that contains doubles i.e. numerical

colour_feelings <- list(
    blue   = c("Sad", "Calm"),
    red    = c("Angry", "Energetic", "Warm"),
    green  = c("Calm", "Nature"),
    yellow = c("Happy", "Warm", "Sunny")
)

list on which each element is a character vector.

colour_feelings$blue
[1] "Sad"  "Calm"
for(item in colour_feelings){
  print(length(item))
}
[1] 2
[1] 3
[1] 2
[1] 3
map(.x = colour_feelings, .f = length)
$blue
[1] 2

$red
[1] 3

$green
[1] 2

$yellow
[1] 3
map(.x = colour_feelings, .f = paste, collapse = ", ")
$blue
[1] "Sad, Calm"

$red
[1] "Angry, Energetic, Warm"

$green
[1] "Calm, Nature"

$yellow
[1] "Happy, Warm, Sunny"
colour_translator <- list(
  blue   = "gorm",
    red    = "dearg",   
    green  = "uaine",
    yellow = "buidhe"   
)

We want to paste “Translation:” in front of each of these words

add_translation <- function(text){
  return(paste("Translation: ", text))
}
add_translation("gorm")
[1] "Translation:  gorm"
map(.x = colour_translator, .f = add_translation)
$blue
[1] "Translation:  gorm"

$red
[1] "Translation:  dearg"

$green
[1] "Translation:  uaine"

$yellow
[1] "Translation:  buidhe"

purrr let’s you define little “bespoke” custom-fitted functions to do wrangling.

map(.x = colour_translator, .f = ~ paste("Transalation: ", .x))
$blue
[1] "Transalation:  gorm"

$red
[1] "Transalation:  dearg"

$green
[1] "Transalation:  uaine"

$yellow
[1] "Transalation:  buidhe"

dataframes in R are just lists where each element is a vector and the label of the element is like the column head.

library(CodeClanData)

Attaching package: ‘CodeClanData’

The following object is masked from ‘package:dplyr’:

    starwars

The following object is masked from ‘package:tidyr’:

    population

The following object is masked from ‘package:datasets’:

    volcano
colour_wavelengths <- list(
    blue   = 470,
    red    = 665,
    green  = 550,
    yellow = 600
)
map(colour_translator, nchar)
$blue
[1] 4

$red
[1] 5

$green
[1] 5

$yellow
[1] 6
map(colour_wavelengths, round, digits = -2)
$blue
[1] 500

$red
[1] 700

$green
[1] 600

$yellow
[1] 600
map(colour_wavelengths, ~ .x/(1*(10^9)))
$blue
[1] 4.7e-07

$red
[1] 6.65e-07

$green
[1] 5.5e-07

$yellow
[1] 6e-07
#colour_feelings
map_dbl(colour_feelings, length)
  blue    red  green yellow 
     2      3      2      3 
map_int(colour_feelings, length)
  blue    red  green yellow 
     2      3      2      3 
flatten_chr(colour_feelings)
 [1] "Sad"       "Calm"      "Angry"     "Energetic" "Warm"      "Calm"     
 [7] "Nature"    "Happy"     "Warm"      "Sunny"    
studentslist <- as.list(students)
students
$age_years
 [1] 17 14 14 17 16 17 13 17 16 13 18 17 17 17

$reaction_time
 [1] 0.4420 0.3940 0.5510 0.3550 0.3810 0.6010 0.5940 0.3940 0.4320 0.2990 0.0774
[12] 1.0000 0.4810 0.4500

$text_messages_sent_yesterday
 [1]  45  28   5  35  15 100  31   0 200   0 100 200  35  20

$favorite_school_subject
 [1] "English"                    "Mathematics and statistics"
 [3] "Mathematics and statistics" "Music"                     
 [5] "Mathematics and statistics" "Physical education"        
 [7] "Physical education"         "Other"                     
 [9] "Art"                        "Music"                     
[11] "Computers and technology"   "English"                   
[13] "Mathematics and statistics" "Science"                   

$school_year
 [1] "Year 11" "Year 9"  "Year 8"  "Year 12" "Year 12" "Year 12" "Year 8"  "Year 12"
 [9] "Year 12" "Year 7"  "Year 12" "Year 12" "Year 12" "Year 11"

$height_cm
 [1] 185.0 160.0 152.4 155.0 176.0 177.8 166.0 164.0 162.0 154.0 183.0 157.0 175.3
[14] 173.0

$sleep_hours_schoolnight
 [1]  8.0  7.0  6.0  8.0  5.5  5.0 10.0  7.0  6.0  8.0  8.0  6.0  6.5  6.0

$superpower
 [1] "Telepathy"    "Invisibility" "Telepathy"    "Fly"          "Freeze time" 
 [6] "Invisibility" "Fly"          "Telepathy"    "Telepathy"    "Invisibility"
[11] "Fly"          "Fly"          "Telepathy"    "Telepathy"   

$languages_spoken
 [1] 1.0 2.0 1.0 1.0 2.0 2.0 2.0 1.0 3.0 2.0 2.0 1.0 1.5 2.0

$planned_education_level
 [1] "Graduate degree"       "Graduate degree"       "Graduate degree"      
 [4] "High school"           "Less than high school" "Graduate degree"      
 [7] "Graduate degree"       "Graduate degree"       "Graduate degree"      
[10] "Graduate degree"       "Graduate degree"       "Graduate degree"      
[13] "Graduate degree"       "Graduate degree"      
students <- map_df(students, sort)

conditional mapping:

blue <- list(
    translation = "gorm",
    feelings    = c("Sad", "Calm"),
    primary     = "Yes",
    wavelength  = 470
)
# p is a predicate = logical i.e. condition/question
map_if(.x = blue, .p = is.character, .f = nchar)
$translation
[1] 4

$feelings
[1] 3 4

$primary
[1] 3

$wavelength
[1] 470
# apply paste only to elements of blue with a length > 1

map_if(.x = blue, .p = ~ length(.x) > 1, .f = paste, collapse = ", ")
$translation
[1] "gorm"

$feelings
[1] "Sad, Calm"

$primary
[1] "Yes"

$wavelength
[1] 470
str(colour_list)
List of 4
 $ blue  :List of 4
  ..$ name      : chr "Blue"
  ..$ feelings  : chr [1:2] "Sad" "Calm"
  ..$ primary   : chr "Yes"
  ..$ wavelength: num 470
 $ red   :List of 4
  ..$ name      : chr "Red"
  ..$ feelings  : chr [1:3] "Angry" "Energetic" "Warm"
  ..$ primary   : chr "Yes"
  ..$ wavelength: num 665
 $ green :List of 4
  ..$ name      : chr "Green"
  ..$ feelings  : chr [1:2] "Calm" "Nature"
  ..$ primary   : chr "No"
  ..$ wavelength: num 550
 $ yellow:List of 4
  ..$ name      : chr "Yellow"
  ..$ feelings  : chr [1:3] "Happy" "Warm" "Sunny"
  ..$ primary   : chr "Yes"
  ..$ wavelength: num 600
map(colour_list, "wavelength")
$blue
[1] 470

$red
[1] 665

$green
[1] 550

$yellow
[1] 600
map(colour_list, "feelings")
$blue
[1] "Sad"  "Calm"

$red
[1] "Angry"     "Energetic" "Warm"     

$green
[1] "Calm"   "Nature"

$yellow
[1] "Happy" "Warm"  "Sunny"

not the best way of doing things

map(colour_list, 4)
$blue
[1] 470

$red
[1] 665

$green
[1] 550

$yellow
[1] 600
colour_list_feelings <- map(colour_list, "feelings")
map(colour_list_feelings, length)
$blue
[1] 2

$red
[1] 3

$green
[1] 2

$yellow
[1] 3

API application programming interface APIs return JSON javascript object notation R converts json to nested list

Colours and themes

http://sape.inf.usi.ch/quick-reference/ggplot2/colour

Also look up hexidecimal colours

ggplot(students) +
  aes(x = reaction_time) +
  geom_histogram(fill = "magenta")

ggplot(students) +
  aes(x = reaction_time) +
  geom_histogram(fill = hcl(200, 50, 50))

ggplot(students) +
  aes(x = reaction_time, y = height_cm) +
  geom_point(colour = "violetred1")

ggplot(pets, aes(weight, age, colour = sex)) +
  geom_point()

ggplot(pets, aes(weight, age, colour = sleep)) +
  geom_point() +
  scale_colour_gradient(low = "grey0", high = "grey100")

if colour is fill then scale is fill (make sense?)

ggplot(pets, aes(weight, age, colour = sleep)) +
  geom_point() +
  scale_colour_gradient(low = "seagreen2", high = "turquoise4")

ggplot(pets, aes(weight, age, colour = sleep)) +
  geom_point() +
  scale_colour_gradient2(low = "blue", high = "red", mid = "white", midpoint = 15)

Geom_raster is a heat map

ggplot(volcano, aes(x = x, y = y, fill = height)) +
  geom_raster() +
  scale_fill_gradientn(colours = c("chartreuse1", "maroon1"))

NA
ggplot(volcano, aes(x = x, y = y, fill = height)) +
  geom_raster() +
  scale_fill_gradientn(colours = colorspace::terrain_hcl(10))

https://colorbrewer2.org/#type=sequential&scheme=OrRd&n=3

ggplot(volcano, aes(x = x, y = y, fill = height)) +
  geom_raster() +
  scale_fill_distiller(palette = "OrRd")

ggplot(temp_df) +
  geom_raster(aes(x = month, y = year, fill = max_temp)) +
  scale_fill_gradient(low = "olivedrab2", high = "darkorchid2")

ggplot(temp_df) +
  geom_raster(aes(x = month, y = year, fill = max_temp)) +
  scale_fill_gradient2(low = "dodgerblue4", high = "deeppink4", mid = "darkorange2", midpoint = 10)

ggplot(temp_df) +
  geom_raster(aes(x = month, y = year, fill = max_temp)) +
  scale_fill_gradientn(colours = colorspace::terrain_hcl(28))

ggplot(temp_df) +
  geom_raster(aes(x = month, y = year, fill = max_temp)) +
  scale_fill_distiller(palette = "Dark2")

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar()

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar() +
scale_fill_hue(h = c(120, 300))

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar() +
  scale_fill_brewer(palette = "Set1")

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar() +
  scale_fill_grey()

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar() +
  scale_fill_grey(start = 0, end = 0.5)

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar() +
  scale_fill_manual(values = c(
    "Fly" = "red",
    "Freeze time" = "blue",
    "Invisibility" = "green",
    "Telepathy" = "yellow"
  ))

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar() +
  scale_fill_manual(values = c("red", "blue", "green", "yellow"))

ggplot(students) +
  aes(x = school_year, fill = superpower) +
  geom_bar() +
  scale_fill_manual( values = wes_palette("Grandbudapest1"))
Error in wes_palette("Grandbudapest1") : 
  could not find function "wes_palette"
ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line()

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  scale_colour_hue(h = c(60, 2000))

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  scale_colour_grey(start = 0, end = 0.7)

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  scale_colour_brewer(palette = "Accent")

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  scale_colour_manual(values = c("red", "blue", "green", "orange"))

guide_colour_bar for continuous data

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  scale_colour_hue(guide = guide_legend(nrow = 3))

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  scale_colour_hue(guide = guide_legend(reverse = TRUE))

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  scale_colour_hue(guide = guide_legend(keywidth = 1, keyheight = 6, reverse = TRUE))

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  theme_grey(base_size = 20)

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  labs(
    title = "Typical Diet of a Chinese Citizen"
  ) +
  theme(
    title = element_text(size = 20, colour = "red", face = "bold")
  )

?theme
ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  labs(
    title = "Typical Diet of a Chinese Citizen"
  ) +
  theme(
    axis.title.x = element_text(size = 20, colour = "red", face = "bold"),
    panel.grid.major = element_line(colour = "black", linetype = "dotted", size = 0.3),
    plot.background = element_rect(fill = "limegreen")
  )

ggplot(chinesemeal) +
  aes(x = Year, colour = FoodType, y = CaloriesPerDay) +
  geom_line() +
  labs(
    title = "Typical Diet of a Chinese Citizen"
  ) +
  theme(
    axis.title.x = element_text(size = 20, colour = "red", face = "bold"),
    panel.grid.major = element_line(colour = "black", linetype = "dotted", size = 0.3),
    plot.background = element_rect(fill = "limegreen"),
    legend.text = element_blank()
  )

install.packages("ggthemes")
trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/ggthemes_4.2.0.tgz'
Content type 'application/x-gzip' length 420298 bytes (410 KB)
==================================================
downloaded 410 KB

The downloaded binary packages are in
    /var/folders/tc/k70prjwj5rs5hjh7zmt604_00000gn/T//RtmpJhNoxO/downloaded_packages
library(ggthemes)
ggplot(scottish_exports) +
  geom_line(aes(x = year, y = exports, colour = sector)) +
  facet_wrap(~sector, scales = 'free_y') +
  theme_excel() +
  theme(
    axis.title.x = element_text(size = 20, colour = "blue", face = "bold"),
    axis.title.y = element_text(size = 20, colour = "blue", face = "bold")
  )

Change order: make into ordered factor

total_sales %>% 
  mutate(branch = factor(branch, levels = c("London", "leeds", "Glasgow", "Leatherhed", "Edinburgh", "Manchester", "Welyn Garden City")))

ggplot(total_sales) +
  aes(x = branch, y = sales) +
  geom_col() +
  coord_flip()

total_sales <- total_sales %>% 
  mutate(branch = fct_reorder(branch, sales))

ggplot(total_sales) +
  aes(x = branch, y = sales) +
  geom_col() +
  coord_flip()

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKUHVycnJycnJycnJycnJycnJycnJycnJycgoKbWFwKCkgaXMgdGhlIG1haW4gb25lLgoKSXRlcmF0ZXMgb3ZlciBhIGRhdGEgc3RydWN0dXJlIGFuZCBydW5zIGEgZnVuY3Rpb24gdGhhdCB5b3UgcHJvdmlkZSBvbiBlYWNoICJlbGVtZW50IiBvZiB0aGUgZGF0YSBzdHJ1Y3R1cmUuIHJlcGxhY2VzICdmb3InIGxvb3BzLgoKbWFwXzxzb21ldGhpbmc+KCkKPHNvbWV0aGluZz4gaXMgZGF0YSB0eXBlIHRoZXkgcmV0dXJuCmUuZy4gbWFwX2RibCgpIHJldHVybiBhIHZlY3RvciB0aGF0IGNvbnRhaW5zIGRvdWJsZXMgaS5lLiBudW1lcmljYWwKCmBgYHtyfQpjb2xvdXJfZmVlbGluZ3MgPC0gbGlzdCgKICAgIGJsdWUgICA9IGMoIlNhZCIsICJDYWxtIiksCiAgICByZWQgICAgPSBjKCJBbmdyeSIsICJFbmVyZ2V0aWMiLCAiV2FybSIpLAogICAgZ3JlZW4gID0gYygiQ2FsbSIsICJOYXR1cmUiKSwKICAgIHllbGxvdyA9IGMoIkhhcHB5IiwgIldhcm0iLCAiU3VubnkiKQopCmBgYAoKbGlzdCBvbiB3aGljaCBlYWNoIGVsZW1lbnQgaXMgYSBjaGFyYWN0ZXIgdmVjdG9yLgoKYGBge3J9CmNvbG91cl9mZWVsaW5ncyRibHVlCmBgYAoKYGBge3J9CmZvcihpdGVtIGluIGNvbG91cl9mZWVsaW5ncyl7CiAgcHJpbnQobGVuZ3RoKGl0ZW0pKQp9CmBgYAoKYGBge3J9Cm1hcCgueCA9IGNvbG91cl9mZWVsaW5ncywgLmYgPSBsZW5ndGgpCmBgYAoKYGBge3J9CiMgcGFzcyBleHRyYSBhcmd1bWVudHMgYWZ0ZXIgLmYKbWFwKC54ID0gY29sb3VyX2ZlZWxpbmdzLCAuZiA9IHBhc3RlLCBjb2xsYXBzZSA9ICIsICIpCmBgYAoKYGBge3J9CmNvbG91cl90cmFuc2xhdG9yIDwtIGxpc3QoCiAgYmx1ZSAgID0gImdvcm0iLAogICAgcmVkICAgID0gImRlYXJnIiwgICAKICAgIGdyZWVuICA9ICJ1YWluZSIsCiAgICB5ZWxsb3cgPSAiYnVpZGhlIiAgIAopCgpgYGAKCldlIHdhbnQgdG8gcGFzdGUgIlRyYW5zbGF0aW9uOiAiIGluIGZyb250IG9mIGVhY2ggb2YgdGhlc2Ugd29yZHMKCmBgYHtyfQphZGRfdHJhbnNsYXRpb24gPC0gZnVuY3Rpb24odGV4dCl7CiAgcmV0dXJuKHBhc3RlKCJUcmFuc2xhdGlvbjogIiwgdGV4dCkpCn0KYGBgCgpgYGB7cn0KYWRkX3RyYW5zbGF0aW9uKCJnb3JtIikKYGBgCgpgYGB7cn0KbWFwKC54ID0gY29sb3VyX3RyYW5zbGF0b3IsIC5mID0gYWRkX3RyYW5zbGF0aW9uKQpgYGAKCnB1cnJyIGxldCdzIHlvdSBkZWZpbmUgbGl0dGxlICJiZXNwb2tlIiBjdXN0b20tZml0dGVkIGZ1bmN0aW9ucyB0byBkbyB3cmFuZ2xpbmcuCgpgYGB7cn0KbWFwKC54ID0gY29sb3VyX3RyYW5zbGF0b3IsIC5mID0gfiBwYXN0ZSgiVHJhbnNhbGF0aW9uOiAiLCAueCkpCmBgYAoKZGF0YWZyYW1lcyBpbiBSIGFyZSBqdXN0IGxpc3RzIHdoZXJlIGVhY2ggZWxlbWVudCBpcyBhIHZlY3RvciBhbmQgdGhlIGxhYmVsIG9mIHRoZSBlbGVtZW50IGlzIGxpa2UgdGhlIGNvbHVtbiBoZWFkLgoKYGBge3J9CmxpYnJhcnkoQ29kZUNsYW5EYXRhKQpgYGAKCmBgYHtyfQpjb2xvdXJfd2F2ZWxlbmd0aHMgPC0gbGlzdCgKICAgIGJsdWUgICA9IDQ3MCwKICAgIHJlZCAgICA9IDY2NSwKICAgIGdyZWVuICA9IDU1MCwKICAgIHllbGxvdyA9IDYwMAopCmBgYAoKYGBge3J9Cm1hcChjb2xvdXJfdHJhbnNsYXRvciwgbmNoYXIpCmBgYAoKYGBge3J9Cm1hcChjb2xvdXJfd2F2ZWxlbmd0aHMsIHJvdW5kLCBkaWdpdHMgPSAtMikKYGBgCgpgYGB7cn0KbWFwKGNvbG91cl93YXZlbGVuZ3RocywgfiAueC8oMSooMTBeOSkpKQojIDFFOSBpcyBzY2llbnRpZmljIG5vdGF0aW9uIGlzIDEgKiAxMF45CmBgYAoKYGBge3J9CiNjb2xvdXJfZmVlbGluZ3MKbWFwX2RibChjb2xvdXJfZmVlbGluZ3MsIGxlbmd0aCkKYGBgCgpgYGB7cn0KbWFwX2ludChjb2xvdXJfZmVlbGluZ3MsIGxlbmd0aCkKYGBgCgpgYGB7cn0KZmxhdHRlbl9jaHIoY29sb3VyX2ZlZWxpbmdzKQpgYGAKCmBgYHtyfQpzdHVkZW50c2xpc3QgPC0gYXMubGlzdChzdHVkZW50cykKc3R1ZGVudHMKYGBgCgpgYGB7cn0Kc3R1ZGVudHMgPC0gbWFwX2RmKHN0dWRlbnRzLCBzb3J0KQpgYGAKCmNvbmRpdGlvbmFsIG1hcHBpbmc6CgpgYGB7cn0KYmx1ZSA8LSBsaXN0KAogICAgdHJhbnNsYXRpb24gPSAiZ29ybSIsCiAgICBmZWVsaW5ncyAgICA9IGMoIlNhZCIsICJDYWxtIiksCiAgICBwcmltYXJ5ICAgICA9ICJZZXMiLAogICAgd2F2ZWxlbmd0aCAgPSA0NzAKKQpgYGAKCmBgYHtyfQojIHAgaXMgYSBwcmVkaWNhdGUgPSBsb2dpY2FsIGkuZS4gY29uZGl0aW9uL3F1ZXN0aW9uCm1hcF9pZigueCA9IGJsdWUsIC5wID0gaXMuY2hhcmFjdGVyLCAuZiA9IG5jaGFyKQpgYGAKCmBgYHtyfQojIGFwcGx5IHBhc3RlIG9ubHkgdG8gZWxlbWVudHMgb2YgYmx1ZSB3aXRoIGEgbGVuZ3RoID4gMQoKbWFwX2lmKC54ID0gYmx1ZSwgLnAgPSB+IGxlbmd0aCgueCkgPiAxLCAuZiA9IHBhc3RlLCBjb2xsYXBzZSA9ICIsICIpCmBgYAoKYGBge3J9CnN0cihjb2xvdXJfbGlzdCkKYGBgCgpgYGB7cn0KbWFwKGNvbG91cl9saXN0LCAid2F2ZWxlbmd0aCIpCmBgYAoKYGBge3J9Cm1hcChjb2xvdXJfbGlzdCwgImZlZWxpbmdzIikKYGBgCiMgbm90IHRoZSBiZXN0IHdheSBvZiBkb2luZyB0aGluZ3MKYGBge3J9Cm1hcChjb2xvdXJfbGlzdCwgNCkKYGBgCgpgYGB7cn0KY29sb3VyX2xpc3RfZmVlbGluZ3MgPC0gbWFwKGNvbG91cl9saXN0LCAiZmVlbGluZ3MiKQptYXAoY29sb3VyX2xpc3RfZmVlbGluZ3MsIGxlbmd0aCkKYGBgCgpBUEkgYXBwbGljYXRpb24gcHJvZ3JhbW1pbmcgaW50ZXJmYWNlCkFQSXMgcmV0dXJuIEpTT04gamF2YXNjcmlwdCBvYmplY3Qgbm90YXRpb24KUiBjb252ZXJ0cyBqc29uIHRvIG5lc3RlZCBsaXN0CgpDb2xvdXJzIGFuZCB0aGVtZXMKCmh0dHA6Ly9zYXBlLmluZi51c2kuY2gvcXVpY2stcmVmZXJlbmNlL2dncGxvdDIvY29sb3VyCgpBbHNvIGxvb2sgdXAgaGV4aWRlY2ltYWwgY29sb3VycwoKYGBge3J9CmdncGxvdChzdHVkZW50cykgKwogIGFlcyh4ID0gcmVhY3Rpb25fdGltZSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAibWFnZW50YSIpCmBgYAoKYGBge3J9CmdncGxvdChzdHVkZW50cykgKwogIGFlcyh4ID0gcmVhY3Rpb25fdGltZSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGwgPSBoY2woMjAwLCA1MCwgNTApKQpgYGAKCmBgYHtyfQpnZ3Bsb3Qoc3R1ZGVudHMpICsKICBhZXMoeCA9IHJlYWN0aW9uX3RpbWUsIHkgPSBoZWlnaHRfY20pICsKICBnZW9tX3BvaW50KGNvbG91ciA9ICJ2aW9sZXRyZWQxIikKYGBgCgoKYGBge3J9CmdncGxvdChwZXRzLCBhZXMod2VpZ2h0LCBhZ2UsIGNvbG91ciA9IHNleCkpICsKICBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KZ2dwbG90KHBldHMsIGFlcyh3ZWlnaHQsIGFnZSwgY29sb3VyID0gc2xlZXApKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gImdyZXkwIiwgaGlnaCA9ICJncmV5MTAwIikKYGBgCmlmIGNvbG91ciBpcyBmaWxsIHRoZW4gc2NhbGUgaXMgZmlsbCAobWFrZSBzZW5zZT8pCgpgYGB7cn0KZ2dwbG90KHBldHMsIGFlcyh3ZWlnaHQsIGFnZSwgY29sb3VyID0gc2xlZXApKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gInNlYWdyZWVuMiIsIGhpZ2ggPSAidHVycXVvaXNlNCIpCmBgYAoKYGBge3J9CmdncGxvdChwZXRzLCBhZXMod2VpZ2h0LCBhZ2UsIGNvbG91ciA9IHNsZWVwKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIiwgbWlkID0gIndoaXRlIiwgbWlkcG9pbnQgPSAxNSkKYGBgCgoKR2VvbV9yYXN0ZXIgaXMgYSBoZWF0IG1hcApgYGB7cn0KZ2dwbG90KHZvbGNhbm8sIGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSBoZWlnaHQpKSArCiAgZ2VvbV9yYXN0ZXIoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoImNoYXJ0cmV1c2UxIiwgIm1hcm9vbjEiKSkKICAKYGBgCgpgYGB7cn0KZ2dwbG90KHZvbGNhbm8sIGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSBoZWlnaHQpKSArCiAgZ2VvbV9yYXN0ZXIoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGNvbG9yc3BhY2U6OnRlcnJhaW5faGNsKDEwKSkKYGBgCgpodHRwczovL2NvbG9yYnJld2VyMi5vcmcvI3R5cGU9c2VxdWVudGlhbCZzY2hlbWU9T3JSZCZuPTMKCmBgYHtyfQpnZ3Bsb3Qodm9sY2FubywgYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IGhlaWdodCkpICsKICBnZW9tX3Jhc3RlcigpICsKICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIk9yUmQiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QodGVtcF9kZikgKwogIGdlb21fcmFzdGVyKGFlcyh4ID0gbW9udGgsIHkgPSB5ZWFyLCBmaWxsID0gbWF4X3RlbXApKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAib2xpdmVkcmFiMiIsIGhpZ2ggPSAiZGFya29yY2hpZDIiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QodGVtcF9kZikgKwogIGdlb21fcmFzdGVyKGFlcyh4ID0gbW9udGgsIHkgPSB5ZWFyLCBmaWxsID0gbWF4X3RlbXApKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImRvZGdlcmJsdWU0IiwgaGlnaCA9ICJkZWVwcGluazQiLCBtaWQgPSAiZGFya29yYW5nZTIiLCBtaWRwb2ludCA9IDEwKQpgYGAKCmBgYHtyfQpnZ3Bsb3QodGVtcF9kZikgKwogIGdlb21fcmFzdGVyKGFlcyh4ID0gbW9udGgsIHkgPSB5ZWFyLCBmaWxsID0gbWF4X3RlbXApKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGNvbG9yc3BhY2U6OnRlcnJhaW5faGNsKDI4KSkKYGBgCgpgYGB7cn0KZ2dwbG90KHRlbXBfZGYpICsKICBnZW9tX3Jhc3RlcihhZXMoeCA9IG1vbnRoLCB5ID0geWVhciwgZmlsbCA9IG1heF90ZW1wKSkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGUgPSAiRGFyazIiKQpgYGAKCmBgYHtyfQpnZ3Bsb3Qoc3R1ZGVudHMpICsKICBhZXMoeCA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc3VwZXJwb3dlcikgKwogIGdlb21fYmFyKCkKYGBgCgpgYGB7cn0KZ2dwbG90KHN0dWRlbnRzKSArCiAgYWVzKHggPSBzY2hvb2xfeWVhciwgZmlsbCA9IHN1cGVycG93ZXIpICsKICBnZW9tX2JhcigpICsKc2NhbGVfZmlsbF9odWUoaCA9IGMoMTIwLCAzMDApKQpgYGAKCmBgYHtyfQpnZ3Bsb3Qoc3R1ZGVudHMpICsKICBhZXMoeCA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc3VwZXJwb3dlcikgKwogIGdlb21fYmFyKCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpCmBgYAoKYGBge3J9CmdncGxvdChzdHVkZW50cykgKwogIGFlcyh4ID0gc2Nob29sX3llYXIsIGZpbGwgPSBzdXBlcnBvd2VyKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9ncmV5KCkKYGBgCgpgYGB7cn0KZ2dwbG90KHN0dWRlbnRzKSArCiAgYWVzKHggPSBzY2hvb2xfeWVhciwgZmlsbCA9IHN1cGVycG93ZXIpICsKICBnZW9tX2JhcigpICsKICBzY2FsZV9maWxsX2dyZXkoc3RhcnQgPSAwLCBlbmQgPSAwLjUpCmBgYAoKYGBge3J9CmdncGxvdChzdHVkZW50cykgKwogIGFlcyh4ID0gc2Nob29sX3llYXIsIGZpbGwgPSBzdXBlcnBvd2VyKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygKICAgICJGbHkiID0gInJlZCIsCiAgICAiRnJlZXplIHRpbWUiID0gImJsdWUiLAogICAgIkludmlzaWJpbGl0eSIgPSAiZ3JlZW4iLAogICAgIlRlbGVwYXRoeSIgPSAieWVsbG93IgogICkpCmBgYAoKYGBge3J9CmdncGxvdChzdHVkZW50cykgKwogIGFlcyh4ID0gc2Nob29sX3llYXIsIGZpbGwgPSBzdXBlcnBvd2VyKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiLCAiZ3JlZW4iLCAieWVsbG93IikpCmBgYAoKYGBge3J9CmdncGxvdChzdHVkZW50cykgKwogIGFlcyh4ID0gc2Nob29sX3llYXIsIGZpbGwgPSBzdXBlcnBvd2VyKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gd2VzX3BhbGV0dGUoIkdyYW5kQnVkYXBlc3QxIikpCmBgYAoKYGBge3J9CmdncGxvdChjaGluZXNlbWVhbCkgKwogIGFlcyh4ID0gWWVhciwgY29sb3VyID0gRm9vZFR5cGUsIHkgPSBDYWxvcmllc1BlckRheSkgKwogIGdlb21fbGluZSgpCmBgYAoKCgpgYGB7cn0KZ2dwbG90KGNoaW5lc2VtZWFsKSArCiAgYWVzKHggPSBZZWFyLCBjb2xvdXIgPSBGb29kVHlwZSwgeSA9IENhbG9yaWVzUGVyRGF5KSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX2NvbG91cl9odWUoaCA9IGMoNjAsIDIwMDApKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2hpbmVzZW1lYWwpICsKICBhZXMoeCA9IFllYXIsIGNvbG91ciA9IEZvb2RUeXBlLCB5ID0gQ2Fsb3JpZXNQZXJEYXkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfY29sb3VyX2dyZXkoc3RhcnQgPSAwLCBlbmQgPSAwLjcpCmBgYAoKYGBge3J9CmdncGxvdChjaGluZXNlbWVhbCkgKwogIGFlcyh4ID0gWWVhciwgY29sb3VyID0gRm9vZFR5cGUsIHkgPSBDYWxvcmllc1BlckRheSkgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiQWNjZW50IikKYGBgCgpgYGB7cn0KZ2dwbG90KGNoaW5lc2VtZWFsKSArCiAgYWVzKHggPSBZZWFyLCBjb2xvdXIgPSBGb29kVHlwZSwgeSA9IENhbG9yaWVzUGVyRGF5KSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiLCAiZ3JlZW4iLCAib3JhbmdlIikpCmBgYAoKZ3VpZGVfY29sb3VyX2JhciBmb3IgY29udGludW91cyBkYXRhCgpgYGB7cn0KZ2dwbG90KGNoaW5lc2VtZWFsKSArCiAgYWVzKHggPSBZZWFyLCBjb2xvdXIgPSBGb29kVHlwZSwgeSA9IENhbG9yaWVzUGVyRGF5KSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX2NvbG91cl9odWUoZ3VpZGUgPSBndWlkZV9sZWdlbmQobnJvdyA9IDMpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2hpbmVzZW1lYWwpICsKICBhZXMoeCA9IFllYXIsIGNvbG91ciA9IEZvb2RUeXBlLCB5ID0gQ2Fsb3JpZXNQZXJEYXkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfY29sb3VyX2h1ZShndWlkZSA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpCmBgYAoKYGBge3J9CmdncGxvdChjaGluZXNlbWVhbCkgKwogIGFlcyh4ID0gWWVhciwgY29sb3VyID0gRm9vZFR5cGUsIHkgPSBDYWxvcmllc1BlckRheSkgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV9jb2xvdXJfaHVlKGd1aWRlID0gZ3VpZGVfbGVnZW5kKGtleXdpZHRoID0gMSwga2V5aGVpZ2h0ID0gNiwgcmV2ZXJzZSA9IFRSVUUpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2hpbmVzZW1lYWwpICsKICBhZXMoeCA9IFllYXIsIGNvbG91ciA9IEZvb2RUeXBlLCB5ID0gQ2Fsb3JpZXNQZXJEYXkpICsKICBnZW9tX2xpbmUoKSArCiAgdGhlbWVfZ3JleShiYXNlX3NpemUgPSAyMCkKYGBgCgpgYGB7cn0KZ2dwbG90KGNoaW5lc2VtZWFsKSArCiAgYWVzKHggPSBZZWFyLCBjb2xvdXIgPSBGb29kVHlwZSwgeSA9IENhbG9yaWVzUGVyRGF5KSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJUeXBpY2FsIERpZXQgb2YgYSBDaGluZXNlIENpdGl6ZW4iCiAgKSArCiAgdGhlbWUoCiAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGNvbG91ciA9ICJyZWQiLCBmYWNlID0gImJvbGQiKQogICkKYGBgCgpgYGB7cn0KP3RoZW1lCmBgYAoKYGBge3J9CmdncGxvdChjaGluZXNlbWVhbCkgKwogIGFlcyh4ID0gWWVhciwgY29sb3VyID0gRm9vZFR5cGUsIHkgPSBDYWxvcmllc1BlckRheSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiVHlwaWNhbCBEaWV0IG9mIGEgQ2hpbmVzZSBDaXRpemVuIgogICkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgY29sb3VyID0gInJlZCIsIGZhY2UgPSAiYm9sZCIpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkb3R0ZWQiLCBzaXplID0gMC4zKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImxpbWVncmVlbiIpCiAgKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2hpbmVzZW1lYWwpICsKICBhZXMoeCA9IFllYXIsIGNvbG91ciA9IEZvb2RUeXBlLCB5ID0gQ2Fsb3JpZXNQZXJEYXkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicygKICAgIHRpdGxlID0gIlR5cGljYWwgRGlldCBvZiBhIENoaW5lc2UgQ2l0aXplbiIKICApICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGNvbG91ciA9ICJyZWQiLCBmYWNlID0gImJvbGQiKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZG90dGVkIiwgc2l6ZSA9IDAuMyksCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJsaW1lZ3JlZW4iKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF9ibGFuaygpCiAgKQpgYGAKCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJnZ3RoZW1lcyIpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2d0aGVtZXMpCmBgYAoKYGBge3J9CmdncGxvdChzY290dGlzaF9leHBvcnRzKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IGV4cG9ydHMsIGNvbG91ciA9IHNlY3RvcikpICsKICBmYWNldF93cmFwKH5zZWN0b3IsIHNjYWxlcyA9ICdmcmVlX3knKSArCiAgdGhlbWVfZXhjZWwoKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBjb2xvdXIgPSAiYmx1ZSIsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgY29sb3VyID0gImJsdWUiLCBmYWNlID0gImJvbGQiKQogICkKYGBgCgpDaGFuZ2Ugb3JkZXI6Cm1ha2UgaW50byBvcmRlcmVkIGZhY3RvcgpgYGB7cn0KdG90YWxfc2FsZXMgJT4lIAogIG11dGF0ZShicmFuY2ggPSBmYWN0b3IoYnJhbmNoLCBsZXZlbHMgPSBjKCJMb25kb24iLCAibGVlZHMiLCAiR2xhc2dvdyIsICJMZWF0aGVyaGVkIiwgIkVkaW5idXJnaCIsICJNYW5jaGVzdGVyIiwgIldlbHluIEdhcmRlbiBDaXR5IikpKQoKZ2dwbG90KHRvdGFsX3NhbGVzKSArCiAgYWVzKHggPSBicmFuY2gsIHkgPSBzYWxlcykgKwogIGdlb21fY29sKCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQp0b3RhbF9zYWxlcyA8LSB0b3RhbF9zYWxlcyAlPiUgCiAgbXV0YXRlKGJyYW5jaCA9IGZjdF9yZW9yZGVyKGJyYW5jaCwgc2FsZXMpKQoKZ2dwbG90KHRvdGFsX3NhbGVzKSArCiAgYWVzKHggPSBicmFuY2gsIHkgPSBzYWxlcykgKwogIGdlb21fY29sKCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCgoKCgoKCgoKCgo=